/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package testkryo.impl;

import java.io.IOException;

import com.esotericsoftware.kryo.Kryo;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import testkryo.ObjectStorer;

/**
 *
 * @author camomot
 */
public class KryoStorer implements ObjectStorer {

  private Path storageFile;
  private Path storagePath = Paths.get("src/data");
  private Kryo kryo = new Kryo();
  private static KryoIndex kryoIndex;
  private Class clazz;

  public Kryo getKryo() {
    return kryo;
  }
  
  public KryoStorer(Class clazz) {
    this.clazz = clazz;
    storageFile = storagePath.resolve(clazz.getName() + ".store");
    try {
      Files.createFile(storageFile);
    } catch (FileAlreadyExistsException fx) {
    } catch (IOException ex) {
      Logger.getLogger(KryoStorer.class.getName()).log(Level.SEVERE, null, ex);
    }
    kryo.register(clazz);
    kryoIndex = new KryoIndex(kryo, clazz);
  }

  @Override
  public void store(long id, Object o) {
    
    if(o == null) return;
    ByteBuffer buffer = ByteBuffer.allocate(256);
    FileChannel fc = null;
    kryo.writeObject(buffer, o);
    buffer.flip();

    try {
      fc = FileChannel.open(storageFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
      long pos = fc.size() == 0 ? 0 : fc.size();
      fc.position(pos);
      fc.write(buffer);
     // System.out.println("TS: write " + o.toString() + ": " + System.nanoTime());
      
      kryoIndex.put(id, kryoIndex.get(id), new Long[]{pos, (long) buffer.limit()});
    } catch (IOException ex) {
      Logger.getLogger(KryoStorer.class.getName()).log(Level.SEVERE, null, ex);
    } finally {
      if (fc != null) {
        try {
          fc.close();
        } catch (IOException ex) {
          Logger.getLogger(KryoStorer.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
    }
  }

  @Override
  public <T> T get(long id, Class<T> c) {
    T o = null;
    FileChannel fc = null;
    Long[] i = kryoIndex.get(id);

    if (i != null) {
      long pos = i[0];
      long len = i[1];

      try {
        fc = FileChannel.open(storageFile, StandardOpenOption.READ, StandardOpenOption.WRITE);

        fc.position(pos);

        ByteBuffer buffer = ByteBuffer.allocate((int) len);

        fc.read(buffer);
        buffer.flip();
        o = (T) kryo.readObject(buffer, c);

      } catch (IOException ex) {
        Logger.getLogger(KryoStorer.class.getName()).log(Level.SEVERE, null, ex);
      } finally {
        if (fc != null) {
          try {
            fc.close();
          } catch (IOException ex) {
            Logger.getLogger(KryoStorer.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
      }
    }
    return o;
  }
}
